By far the most read (and best) instructional book on Forth is STARTING FORTH, by Leo Brodie. It is very clear, amusing, and covers the territory. Unfortunately, since Starting Forth was written, Forth itself has undergone considerable evolution, so not everything in Starting Forth is applicable today. Additionally, Starting Forth is based on a particular dialect of Forth (POLY-FORTH), which is a Forth implementation by Forth Inc., the company Mr. Brodie worked for when he wrote his excellent book. As a result, even for the time it was written, certain passages and examples would not work on anything but a Forth Inc. system. ( .S is a classic example of this, but there are others. )
Blazin' Forth is written to support the Forth-83 standard. This is the most recent available standard, and the one which in my opinion, and from all I can tell, the opinion of most other Forth users, the most significant standard so far. As a result, it is not completely compatible with Starting Forth. ( Mr. Brodies latest book THINKING FORTH , uses Forth-83, by the way.)
Perhaps surprisingly, most of the examples Brodie gives work without a single change in Blazin' Forth. This document is intended to help you when the examples don't work as described, speeding up the learning process, easing the pain, and generally helping to prevent you from damaging yourself or your computer through frustation. Differences between the earlier Forth's and the present standard are described, and alternate definitions are provided.
Not all the comments are about differences - there are many more words in Blazin' Forth than are included in the Standard. I couldn't resist telling you about a few of them.
Good luck! And may the Forth be with you.
*ln2
SDB
NY NY 1985
*fp0
*cn1;Chapter 1*cn0
Page 11
The definition STAR will work as supplied. Blazin' Forth contains a word called ASCII that can make words like STAR more readable (and save you a lot of time looking up characters in ASCII tables). Using ASCII , the definition of STAR would be:
: STAR ASCII * EMIT ;
Try it - you'll like it.
By the way, if you change the definition of MARGIN on the next page to
: MARGIN CR 15 SPACES ;
your letter will look a little better on the screen.
*ln2
The Dictionary Page-16
*ln2
Blazin' Forth normally allows 31 characters to be saved in a definitions name. (This is the number specified by the 83 standard.) However, should you prefer fewer characters to be saved, you can do this by changing the value of the variable WIDTH . For example, to have Blazin' Forth save only three characters of your definitions names, type 3 WIDTH !
*ln2
Say-What?
*ln2
Regarding the second foot-note on page-18. The Forth-83 ." will not execute outside of a colon definition. Forth-83 provides the word .( for the occasions when you need to display strings outside of colon definitions. Use it like this:
.( PRINT ME )
*ln2
*cn1;Chapter-2*cn0
*ln2
The handy hint on page-50 is a little notorious. I have, personally, never seen a system on which this definition would work. .S is never the less a very handy word, and Blazin' Forth contains it, already compiled into the system. Note that the extra 0 won't be there - you see only what's on the stack, or you get a message "STACK EMPTY", if nothing is on the stack.
*ln2
*cn1;Chapter-3*cn0
*ln2
Blazin' Forth's editor is a "Starting-Forth-Clone". You will be able to follow this chapter pretty much as it stands. One very important point - you must issue the command MOUNT , to initialize the virtual memory system, before using any words that access the disk - like LIST , for example. Just remember to type MOUNT , and you will be OK . (You only have to give this command once - at the start of your editing session, unless you change disks, issue the command RESTART, or inadvertantly cause a disk error.)
Some general points: Brodie likes to keep his screen numbers high - in the 100's or 200's . Note that if you have a dual drive, such as a 4040 or MSD , you will have no problem with these high screen numbers ( of course, you will have to have a disk in drive 1!). However, if you are using the system with a single disk drive, such as a 1541, then the highest screen that can be accessed from that drive is 165 . Just keep all your screen numbers below this value, and you will be OK .
The WIPE command is very important, never edit a new screen without using this command first! It's so important, that Blazin Forth contains the word W , which is just a short way to perform a WIPE . Works just the same.
Finally, due to the characteristics of the 64's operating system, trailing blanks are suppressed, and not passed to the editor. This means, for example, that Brodies example of blanking a line:
P bb <return>
won't work. Trailing blanks must be followed by an ^ character in order to be received by the editor. For example:
P bb^ ( blanks current line )
U bb^ ( blanks line under current line)
That's enough for now - check back here when you get to FLUSH.
In Forth-83, FLUSH and SAVE-BUFFERS are not quite the same. Both save any updated blocks to the disk, but FLUSH will "hose down" the buffers - if you want to access the same blocks again, Forth will have to re-read them. SAVE-BUFFERS will leave all the blocks current - Forth won't have to re-read them if you need to access them again. Also, Blazin' Forth contains an additional comment word, // , which can be used to include 1 line comments in source screens.
*ln2
Getting Loaded Page-81
*ln2
The suggestion here is not really very good. If the source for your text takes up more than one block, the best solution is not to use many load commands, as Brodie suggests, but to use THRU .
THRU takes two numbers on the stack. The first number is the first screen to load, and the second number is the last screen to load. THRU will then load these two screens, and all screens in between.
So, instead of putting 180 LOAD 181 LOAD 182 LOAD on your load screen, use:
180 182 THRU
instead. It's really much better.
*ln3
*cn1;Chapter 4*cn0
*ln3
A Closer Look at IF . Page-95
One of the big differences between the 83 standard and earlier Forths is that Forth-83 returns a negative 1 ( -1 ) instead of 1 when the result of a test like 0= or 0< is true. So Brodies examples will look like this:
5 4 > . -1 OK
5 4 < . 0 OK
NOT works differently too. In earlier Forths, it was really just another name for 0= . But in Forth-83, it returns the ones-compliment of the number on the top of the stack. Notice that the '83 NOT still reverses the value of the '83 flags:*ln2
-1 NOT . 0 OK
0 NOT . -1 OK
*ln2
But NOT won't change any non-zero value to false ( 0 ) like the older NOT would:*ln2
1 NOT . -2 OK
*ln2
And, of course, FORTH will still regard -2 as being TRUE , since it isn't zero.
So when upgrading programs, or reading books that talk about earlier Forth systems, keep this in mind. On the other hand, if you're not quite sure what all this means, and want to play it safe, just replace every pre-83 NOT with 0= , and you'll be OK .
*ln2
A Little Logic Page-97
*ln2
To upgrade these examples to FORTH-83, just replace every occurance of 1 with -1 , and you'll have a Forth-83 example. While we are on the subject, Blazin' Forth contains the two flags pre-compiled as constants. TRUE leaves a true value (-1) on the top of the stack, and FALSE leaves a false value ( 0 ). You'll appreciate this more when you learn about constants later in the book.
*ln2
Two Words with built in IFs Page 102
*ln2
Just a note - if you do want to add stack checking to your application, all you need to add in Blazin' Forth is the word ?STACK . The ABORT" isn't needed, since ?STACK itself contains the necessary ABORT" .
*ln2
Problems Page-104
*ln2
Problem 5
Actually, in Forth-83, 0 STARS won't print just one star, but 65,535 stars. Brodies answer ( or the one you cook up for yourself) should fix this problem, but there are better ways. More on this when we get to the chapter on LOOPs. Incidentally, if you tried 0 STARS before reading this note, you can get control of things by hitting RUN/STOP RESTORE , just like in BASIC .
*ln2
Chapter-5
The Return Stack
Page-110
The definitions for I , I' and J are not correct for Forth-83. I' does not exist, and I and J are both defined differently. Don't worry, they still do what they are supposed to (which is return the current index value of a loop), it's just that they don't do what Brodie says they would. Just wait till chapter 6 for more information on these two words.
The correct word to copy the top of the return stack is R@ in Forth-83. (i.e. R@ does what Brodie says I should do.)
Page-111
The phrase >R SWAP I would be >R SWAP R@ in Forth - 83. Both phrases will crash the system
Page-112
The definition of QUADRATIC should be:
: QUADRATIC ( A B C X -- N )
>R SWAP ROT R@ * + R> * + ;
In other words, R@ should be used instead of I . See the note above for an explanation.
Page-119
The footnote on this page contains a typo in my copy. The correct definition is:*ln2
: R% 50 */ 1+ 2/ ;
*ln2
Problems
Problem 1
In the answer to this problem, Brodie means NEGATE , not MINUS .
You might also notice that Blazin' Forth does not return a -17 like Brodie says it should. This is due to the 83-standards use of floored division. In floored division, numbers are always truncated to the next lower value. To get the same result as Brodie in 83-Forth systems, use ABS and then NEGATE.
*ln2
*cn1;Chapter 6*cn0
*ln2
Forth-83 uses a new loop structure which is very fast, but differs in a few points from the older loops used in older Forths. Sorry about this, but there will be a little more information here than previously.
*ln2
Page-130
*ln2
Earlier (in the notes to chapter 5) we said that "I" works differently in '83 Forths. Well, originally, "I" was meant to be used as it is here, to access the loop index. Since in those days, the loop index was stored on the top of the return stack, you could also use "I" to copy that value, even if you weren't in a loop, which was not a purpose for which it was originally intended. So that's the difference, I now only returns the loop index, while R@ copies the top of the return stack to the parameter stack. (For the curious: In Blazin' Forth, I returns the sum of the top two return stack items. It may seem strange, but it works just fine!)
The terminating condition for loops is also different than in earlier Forths. Don't be fooled by the fact that most of the examples in this chapter work the same - the exit condition for the loops are different, and if you ignore this fact, you are going to get bit by it.
In '83 loops, the loop is finished when the Index crosses the boundary between the LIMIT and LIMIT - 1 . A couple of examples:
If the Index is 0 , and the LIMIT is 10 , then the loop will terminate when the Index is incremented from 9 to 10 (crosses the boundary).
If the Index is 0 , and the Limit is -10 , then the loop will terminate when the index is incremented from -11 to -10 or decremented from -10 to -11 . This means, if you are using a DO..LOOP, that your loop will take the long way around...0 1 2 3 4 ...32767 -32768 .....-15 -14 -13 -12 -11 . If you are using +LOOP with a negative step value, then your loop will look like this: 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 (boundary crossed, and looped terminated).
*ln2
One odd side effect of this new loop structure is that anytime the limit and the index are equal, the loop will execute 65,535 times. So, for example, the word STARS :
: STARS 0 DO ASCII * EMIT LOOP ;
If used like this:
0 STARS
will EMIT an awful lot of stars. Blazin' Forth contains a word to handle cases like this - ?DO . ?DO acts just like DO , except when the loop arguments are equal. If the arguments are equal, then the loop won't be entered at all - FORTH will jump to the word following LOOP or +LOOP . In addition to keeping out nasty surprises (like 0 STAR) ?DO can also shorten and speed up your code. For example, we often find we don't want to perform a loop when the LIMIT is zero (as in STARS , above.) The traditional way to do this is:
: STARS ?DUP IF 0 DO ASCII * EMIT LOOP THEN ;
But you can now do it this way:
: STARS 0 ?DO IF ASCII * EMIT LOOP ;
You can think of ?DO as having a built in IF .
*fp0
Page 135
The definition given by Brodie:
: TEST 100 10 DO I . -1 +LOOP ;
will execute quite a few more times than one (in '83 Forth). Remember, '83 loops stop when the boundary is crossed between the LIMIT and LIMIT-1 . This crossing can be in either direction, either down ( 101 100 (crossed to 99, so exit) ) or up ( 98 99 (crossed to 100, so exit)) . Since TEST counts down, it will proceed all the way around the number circle, starting with 10, and then going negative, until it eventually goes 101 100 (as in our first example, above). Just though I would mention this.
*ln2
Page-140 LEAVE
*ln2
In '83 Forth, LEAVE leaves immediately, not at the next execution of LOOP or +LOOP. Brodies examples will work the same for both '83 and earlier Forths, but problems can arise if you aren't aware of the difference. For example:
*ln2
: FOO 0 DO I DUP 100 = IF LEAVE THEN . LOOP ;
FOO is a word that will exit when the loop counter reaches 100 . Notice, however, that the actual behaviour of FOO will differ between '83 and earlier standards. In earlier Forths, FOO's execution would be this:
When the Counter reaches 100, LEAVE will set the index equal to the limit.
" . " will be executed, printing 100 on the terminal screen, and leaving the stack empty.
LOOP will execute, and discover its time to stop (since LEAVE changed the INDEX).
In '83 Forth, this is what happens.
When the Counter reaches 100, LEAVE will jump to the word past LOOP , which in this case is the end of the definition. 100 will not be printed, but left on the stack. Quite different behaviours result, as you can see. Just remember that the 83 LEAVE leaps , and you'll be OK .
By the way, Blazin' Forth has a handy word ?LEAVE . ?LEAVE will cause an exit from the loop if the flag on top of the stack is true, otherwise, the loop continues undisturbed. This can shorten your code quite a bit. Here's an example:
: BAR 0 DO I 100 = IF LEAVE THEN LOOP ;
This word just leaves when the index reaches 100. Using ?LEAVE would make things a little nicer:
: BAR 0 DO I 100 = ?LEAVE LOOP ;
Works just the same. Just remember that you have to leave a flag on the stack for ?LEAVE to eat.
*ln2
*cn1;Chapter 7*cn0
*ln2
Section II
Page 161
The Forth-83 word for U* is UM* . It works the same as U* , but has been renamed.
The Forth-83 word for U/MOD is UM/MOD . It works the same.
*ln2
/LOOP is not included. It is no longer necessary in '83 systems. (For the curious: /LOOP was essentially a kludge to fix a problem with the older loops. Just use +LOOP instead.)
*ln2
Page 164
*ln2
'83 systems will only accept the period as punctuation in numbers. However, in Blazin' Forth, you may change this. Here are the steps to follow to get Blazin' Forth to accept the same characters those given by Brodie:
First, type in and LOAD the following definition:
: NEW-PUNCT ( CHAR -- FLAG)
ASCII : OVER = SWAP
ASCII + ASCII 0 >R OVER < SWAP R> < AND OR ;
Then, once your NEW-PUNCT has been loaded, type the following:
' NEW-PUNCT IS PUNCT?
Type the above carefully, or you could cause problems (don't forget the ' ). Blazin' Forth will now accept numbers as described in this section. If you prefer this new version, you may save the system with the word SAVE-FORTH. If you want to change back to the oolder version, do the following:
' (PUNCT?) IS PUNCT?
*ln2
Number Formatting -- Double Length Unsigned.
Pages 167-168
All of the definitions in this section will work as described, but they would be so much more readable using ASCII . Here are some examples:
: .PH# <# # # # # ASCII - HOLD #S #> TYPE SPACE ;
: .DATE <# # # ASCII / HOLD # # ASCII / HOLD #S #> TYPE SPACE ;
: :00 # SEXTAL # DECIMAL ASCII : HOLD ;
*ln2
Page 171
The definition that Brodie gives for .$ won't work in 83-forth. SIGN works on the value on the top of the stack. A definition that will work in 83-Forth is:
: .$ SWAP OVER DABS
<# # # ASCII . HOLD #S ROT ( to bring signed number to top)
ASCII $ HOLD SIGN #> TYPE SPACE ;
You might also noticw that the positions of the $ and SIGN have been switched. I prefer my negative dollars to have their signs in front of the $ sign. That's the only reason for this change.
*ln2
Page 174
The warning to experimenters is not true in Blazin' Forth. You can include punctuated, double-precision numbers inside of colon definitions, and they will work just fine. (I have never understood this restriction.) Note that other systems may not allow this.
*ln2
Page 175
The definition for R% on this page is simply wrong. It should be, instead:
: R% 10 M*/ 5 M+ 10 M/ S>D ROT DROP ;
Which gives $148.15 as an answer.
*fp0
*cn1;Chapter 8*cn0
*ln2
Page 194
See the note above (page 174). You do not need to use 2CONSTANT to include double numbers in Blazin' Forth. You may wish to do so as a matter of style, but it is not forced on you.
*ln2
*cn1;Chapter 9*cn0
*ln2
Brodies example:
110 ' LIMIT !
won't work with the '83 tick. Use
110 ' LIMIT >BODY !
instead. By the way, LIMIT is an important system constant in Blazin' Forth, and I don't recommend that you try this example on it.
*ln2
Vectored Execution:
*ln2
Page 218
The behaviour of ' and ['] in '83 Forth is exactly that described here. Ignore the footnote on '79s tick. You might also wish to look at the documentation for DEFER IS , which are very handy words for handling Vectored Execution.
*ln2
The Structure of a Dictionary Entry.
*ln2
Page 220
The structure of a dictionary entry for Blazin' Forth than described here. (Note: The '83 standard regards this as a system dependency, and does not specify any particular form of dictionary structure. Other systems may, and probably do, differ.)
In Blazin' Forth, the structure is:
LINK FIELD
NAME FIELD
CODE POINTER FIELD
PARAMETER FIELD
There is also an optional field, called the VIEW or LOCATE field. This field is used by LOCATE, and is added or not at the option of the user. With the view field included, the structure looks like this:
VIEW FIELD
NAME FIELD
CODE POINTER FIELD
PARAMETER FIELD
Compiling without the VIEW FIELD results in more compact (but not faster running) definitions, but note that LOCATE will not operate on any definition without a VIEW FIELD. To prevent the view field from being compiled:
VIEW OFF
To re-enable the compilation of the VIEW FIELD:
2 VIEW !
*ln2
Parameter Field
*ln2
Page 223
The address returned by tick and expected by EXECUTE is not the parameter field address, but the CODE FIELD address. If you want the parameter field address, you may convert the address returned by tick (the code field address) to the parameter field address using the '83 standard word >BODY . For an illustration of its use, see the example using LIMIT , given earlier.